﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace TTApp.Phone.Silverlight.Hexagons
{
    /// <summary>
    /// Thể hiện trạng thái hiện tại của Hexagon, có thể là đang chuẩn bị nhảy, hoặc là đang chuẩn bị sinh 
    /// Hexagon khác.
    /// </summary>
    public enum HexagonActionState
    {
        /// <summary>
        /// Thể hiện Hexagon này đang chuẩn bị sinh ra Hexagon khác.
        /// </summary>
        Spawning,
        /// <summary>
        /// Hexagon đang chuyển về trạng thái bình thường sau khi sinh.
        /// </summary>
        Restoring,
        /// <summary>
        /// Thể hiện Hexagon này chuẩn bị nhảy sang một HexagonSlot khác.
        /// </summary>
        Jumping,
        /// <summary>
        /// Thể hiện Hexagon này đang xuất hiện trên màn hình. Là trạng thái của Hexagon
        /// được sinh bởi 1 Hexagon khác.
        /// </summary>
        Appearing,
        /// <summary>
        /// Hexagon này đang nhàn cư vi bất thiện :|
        /// </summary>
        None
    }

    /// <summary>
    /// Đại diện cho 1 quân cờ của người chơi.
    /// </summary>
    public partial class Hexagon : UserControl
    {
        #region //----------------------------- Class Private Fields --------------------------------------//
        /// <summary>
        /// Người chơi sở hữu Hexagon này.
        /// </summary>
        private Player _owner;

        /// <summary>
        /// Các nước đi có thể của hexagon. Dùng để lưu lại các vị trí có thể đến. Nếu có 1 hexagon
        /// nào đó nhảy vào 1 trong các vị trí này khi hexagon hiện tại chưa bất hoạt thì sẽ dẫn đến
        /// HexagonSlot chứa hexagon vừa nhảy vào sẽ không được ẩn background.
        /// </summary>
        private List<HexagonSlot> _oldMovableSlot;

        /// <summary>
        /// Biến toàn cục lưu vị trí đích nhảy hoặc vị trí sinh của Hexagon.
        /// </summary>
        private HexagonSlot _targetSlot;

        /// <summary>
        /// Biến lưu lại Hexagon sẽ được sinh ra bởi Hexagon này.
        /// </summary>
        private Hexagon _spawnedHexagon;

        /// <summary>
        /// Thể hiện trạng thái của Hexagon.
        /// </summary>
        private bool _isActive;

        /// <summary>
        /// Thể hiện trạng thái hiện tại của Hexagon.
        /// </summary>
        private HexagonActionState _state = HexagonActionState.None;
        #endregion

        #region //----------------------------- Dependency Properties -------------------------------------//
        public static readonly DependencyProperty ImageProperty;
        #endregion

        #region //----------------------------- Public Event Delegates ------------------------------------//
        /// <summary>
        /// Phát ra khi ảnh của Hexagon thay đổi.
        /// </summary>
        public event RoutedPropertyChangedEventHandler<Image> ImageChanged;

        /// <summary>
        /// Phát ra khi chủ sở hữu của Hexagon thay đổi.
        /// </summary>
        public event EventHandler<PlayerChangedEventArgs> OwnerChanged;

        /// <summary>
        /// Phát ra khi sau khi Hexagon đã nhảy xong hoặc Hexagon vừa được sinh ra và đã
        /// hoàn thành xong quá trình xuất hiện trên màn hình.
        /// </summary>
        public event EventHandler Appeared;

        /// <summary>
        /// Phát ra khi Hexagon đang chuẩn bị xuất hiện trên màn hình.
        /// </summary>
        public event EventHandler Appearing;
        #endregion

        #region //----------------------------- Class Static Constructor ----------------------------------//
        static Hexagon()
        {
            ImageProperty = DependencyProperty.Register("Image",
                typeof(Image), typeof(Hexagon), new PropertyMetadata(ImageProperty_Changed));
        }
        #endregion

        #region //----------------------------- Class Public Constructors ---------------------------------//
        /// <summary>
        /// Khởi tạo 1 đối tượng Hexagon mới.
        /// </summary>
        public Hexagon()
        {
            InitializeComponent();
            LayoutUpdated += new EventHandler(OnLayoutUpdated);
        }
        #endregion

        #region //----------------------------- Class CLR Properties --------------------------------------//
        /// <summary>
        /// Hình ảnh biểu thị Hexagon.
        /// </summary>
        [TypeConverter(typeof(ImageConverter))]
        public Image Image
        {
            get { return GetValue(ImageProperty) as Image; }
            set { SetValue(ImageProperty, value); }
        }

        /// <summary>
        /// Lấy chủ sở hữu của Hexagon này.
        /// Không gán trực tiếp được, phải dùng phương thức Seize() của Player. 
        /// Muốn bỏ quyền sở hữu thì dùng Release() của Player.
        /// </summary>
        public Player Owner
        {
            get
            {
                return _owner;
            }
            set
            {
                Player newOwner = value;
                if (newOwner == null)
                {
                    // Tồn tại chủ cũ
                    if (_owner != null && _owner.IsOwn(this))
                    {
                        throw new InvalidOperationException("Không bỏ được trực tiếp, sử dụng phương thức Release() của Player.");
                    }
                }
                else if (!newOwner.IsOwn(this))
                {
                    throw new InvalidOperationException("Không gán trực tiếp, sử dụng phương thức Seize() của player để thay thế.");
                }

                Player oldOwner = _owner;
                _owner = newOwner;

                if (oldOwner != newOwner)
                    OnOwnerChanged(new PlayerChangedEventArgs(oldOwner, newOwner));
            }
        }

        /// <summary>
        /// Lấy trạng thái hiện tại của Hexagon.
        /// </summary>
        public bool IsActive
        {
            get { return _isActive; }
        }
        #endregion

        #region //----------------------------- Private Event Handlers ------------------------------------//
        /// <summary>
        /// Căn chỉnh vị trí của Hexagon.
        /// </summary>
        private void OnLayoutUpdated(object sender, EventArgs args)
        {
            HexagonSlot parent = VisualTreeHelper.GetParent(this) as HexagonSlot;
            if (parent != null)
            {
                image.Width = parent.ActualWidth * 0.65;
                image.Height = parent.ActualHeight * 0.65;
            }
        }

        /// <summary>
        /// Được gọi sau khi trong giai đoạn thứ 2 của việc nhảy của Hexagon.
        /// Làm cho Hexagon xuất hiện ở vị trí khác của Board.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnScaleStoryboardCompleted(object sender, EventArgs e)
        {
            switch (_state)
            {
                case HexagonActionState.Jumping:
                    {
                        Deactive();

                        // Xóa nó ra khỏi slot hiện tại và thêm vào ở slot mới.
                        HexagonSlot parent = VisualTreeHelper.GetParent(this) as HexagonSlot;
                        parent.Hexagon = null;
                        _targetSlot.Hexagon = this;

                        // Bắt đầu animation hiện.
                        bubbleScaleXAnimation.From = bubbleScaleYAnimation.From = 0;
                        bubbleScaleXAnimation.To = bubbleScaleYAnimation.To = 1;

                        OnAppearing(EventArgs.Empty);
                        bubbleStoryboard.Begin();

                        rotateStoryboard.SpeedRatio = 100;
                        rotateStoryboard.Begin();

                        // Xóa biến lưu đích <- comment thừa vãi :|
                        _targetSlot = null;

                        break;
                    }
                case HexagonActionState.Appearing:
                    {
                        rotateStoryboard.Stop();
                        OnAppeared(EventArgs.Empty);
                        break;
                    }
                case HexagonActionState.Spawning:
                    {
                        Deactive();

                        _spawnedHexagon.StartAppearAnimation(_targetSlot);

                        // Đưa Hexagon này trở về trạng thái bình thường sau 0.35 giây.
                        scaleXAnimation.From = scaleYAnimation.From = 0.5;
                        scaleXAnimation.To = scaleYAnimation.To = 1;

                        backXEase.EasingMode = backYEase.EasingMode = EasingMode.EaseInOut;

                        // Trạng thái sau khi sinh.
                        _state = HexagonActionState.Restoring;

                        scaleStoryboard.BeginTime = TimeSpan.FromSeconds(0.14);
                        scaleStoryboard.Begin();

                        // Xóa biến.
                        _spawnedHexagon = null;
                        break;
                    }
                case HexagonActionState.Restoring:
                    {
                        break;
                    }
            }

            _state = HexagonActionState.None;
        }

        /// <summary>
        /// Được gọi khi Hexagon này đã hoàn thành xong quá trình xuất hiện sau khi nhảy.
        /// </summary>
        private void OnBubbleStoryboardCompleted(object sender, EventArgs e)
        {
            rotateStoryboard.Stop();
            OnAppeared(EventArgs.Empty);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender">Hexagon đã được sinh ra.</param>
        /// <param name="args">Empty.</param>
        private void OnSpawnedHexagonAppeared(object sender, EventArgs args)
        {
            
        }

        /// <summary>
        /// Dừng việc quay Hexagon lại.
        /// </summary>
        private void OnOpacityChangeStoryboardCompleted(object sender, EventArgs e)
        {
            rotateStoryboard.Stop();
        }
        #endregion

        #region //----------------------------- Overridden Methods ----------------------------------------//
        protected override void OnTap(GestureEventArgs args)
        {
            base.OnTap(args);
        }
        #endregion

        #region //----------------------------- Protected Event Invokers ----------------------------------//
        /// <summary>
        /// Được gọi khi hình ảnh đại diện Hexagon này bị thay đổi.
        /// </summary>
        /// <param name="oldValue">Ảnh cũ.</param>
        /// <param name="newValue">Ảnh mới.</param>
        protected virtual void OnImageChanged(Image oldValue, Image newValue)
        {
            image.Source = newValue.Source;

            if (ImageChanged != null)
            {
                ImageChanged(this, new RoutedPropertyChangedEventArgs<Image>(oldValue, newValue));
            }
        }

        /// <summary>
        /// Được gọi khi chủ sở hữu của Hexagon này thay đổi.
        /// </summary>
        protected virtual void OnOwnerChanged(PlayerChangedEventArgs args)
        {
            if (OwnerChanged != null)
            {
                OwnerChanged(this, args);
            }
        }

        /// <summary>
        /// Được gọi ngay sau khi animation hiển thị hexagon này kết thúc.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnAppeared(EventArgs args)
        {
            if (Appeared != null)
            {
                Appeared(this, args);
            }
        }

        /// <summary>
        /// Phát ra khi Hexagon này chuẩn bị xuất hiện trên màn hình.
        /// </summary>
        /// <param name="args"></param>
        protected virtual void OnAppearing(EventArgs args)
        {
            if (Appearing != null)
            {
                Appearing(this, args);
            }
        }
        #endregion

        #region //----------------------------- DP Changed Callbacks --------------------------------------//
        private static void ImageProperty_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            (obj as Hexagon).OnImageChanged(args.OldValue as Image, args.NewValue as Image);
        }
        #endregion

        #region //----------------------------- Class Public Methods --------------------------------------//
        /// <summary>
        /// Lấy các hexagon hàng xóm gần nhất của hexagon hiện tại.
        /// </summary>
        /// <returns>
        /// Nếu Hexagon chưa được đặt vào HexagonSlot, cả 6 giá trị sẽ là null.
        /// Các giá trị hàng xóm được xếp theo vòng tròn theo thứ tự thuận chiều kim đồng hồ:
        ///     top -> topright -> right -> bottomright -> bottom -> bottomleft -> left -> topleft.
        /// </returns>
        public List<Hexagon> GetNeighbours()
        {
            return GetNeighbours(1);
        }

        /// <summary>
        /// Lấy hàng xóm (các Hexagon) theo cấp của Hexagon hiện tại.
        /// </summary>
        /// <param name="distance">Khoảng cách (cấp) đến hàng xóm muốn lấy.</param>
        /// <returns>
        /// List chứa các hàng xóm. Nếu ở vị trí nào không tồn tại Hexagon thì sẽ 
        /// mang giá trị null.
        /// </returns>
        public List<Hexagon> GetNeighbours(int distance)
        {
            if (distance < 1)
            {
                throw new ArgumentOutOfRangeException("distance", "Khoảng cách phải lớn hơn hoặc bằng 1.");
            }

            int total = 6 * distance;

            List<Hexagon> neighbours = new List<Hexagon>(total);
            for (int i = 0; i < total; i++)
            {
                neighbours.Add(null);
            }

            HexagonSlot parent = VisualTreeHelper.GetParent(this) as HexagonSlot;
            if (parent != null)
            {
                List<HexagonSlot> slots = parent.GetNeighbours(distance);
                for (int i = 0; i < slots.Count; i++)
                {
                    if (slots[i] != null)
                    {
                        neighbours[i] = slots[i].Hexagon;
                    }
                }
            }

            return neighbours;
        }

        /// <summary>
        /// Lấy các ô HexagonSlot có thể đi đến được của Hexagon này. 
        /// Lớp kế thừa có thể ghi đè để thể hiện các kiểu đi khác nhau của Hexagon.
        /// </summary>
        /// <returns>Tập các HexagonSlot là bước đi có thể đến của Hexagon hiện tại.</returns>
        public virtual List<HexagonSlot> GetMovableSlot()
        {
            HexagonSlot parent = VisualTreeHelper.GetParent(this) as HexagonSlot;
            if (parent == null)
            {
                throw new InvalidOperationException("Hexagon chưa được đặt vào slot");
            }

            List<HexagonSlot> movableSlots = new List<HexagonSlot>();

            foreach (HexagonSlot slot in parent.GetNeighbours(1))
            {
                if (slot != null && slot.IsEmpty)
                {
                    movableSlots.Add(slot);
                }
            }

            foreach (HexagonSlot slot in parent.GetNeighbours(2))
            {
                if (slot != null && slot.IsEmpty)
                {
                    movableSlots.Add(slot);
                }
            }

            return movableSlots;
        }

        /// <summary>
        /// Hiển thị các ô có thể di chuyển của Hexagon này. Lớp kế thừa có thể override phương thức này
        /// để thể hiện nước có thể di chuyển của 1 Hexagon đặc thù.
        /// </summary>
        /// <returns>true nếu 1 nhất có 1 ô có thể đi đến.</returns>
        public virtual bool ShowMovableSlot()
        {
            _oldMovableSlot = GetMovableSlot();

            if (_oldMovableSlot.Count == 0)
            {
                return false;
            }

            foreach (HexagonSlot slot in _oldMovableSlot)
            {
                BackgroundDecoration decoration = 
                    slot.GetDecoration("BackgroundDecoration") as BackgroundDecoration;

                if (IsNeighbourWith(slot, 1))
                {
                    decoration.ShowOnlyImage(0);
                }
                else if (IsNeighbourWith(slot, 2))
                {
                    decoration.ShowOnlyImage(1);
                }
            }

            return true;
        }

        /// <summary>
        /// Ẩn đi trạng thái biểu diễn ô có thể di chuyển của Hexagon hiện tại.
        /// Lớp kế thừa có thể override phương thức này để thể cách ẩn của Hexagon đặc trưng.
        /// </summary>
        public virtual void HideMovableSlot()
        {
            foreach (HexagonSlot slot in _oldMovableSlot)
            {
                BackgroundDecoration decoration =
                    slot.GetDecoration("BackgroundDecoration") as BackgroundDecoration;

                decoration.HideAllImages();
            }

            _oldMovableSlot = null;
        }

        /// <summary>
        /// Kích hoạt Hexagon. Một số hành động có thể xảy ra như hiện các nước có thể di chuyển,
        /// hiện animation...
        /// Lớp kế thừa có thể override phương thức này để cài đặt hành động riêng cho Hexagon đó.
        /// </summary>
        public virtual void Active()
        {
            if (ShowMovableSlot())
            {
                rotateStoryboard.SpeedRatio = 1;
                rotateStoryboard.Begin();
            }
            else
            {
                rotateStoryboard.SpeedRatio = 0.3;
                rotateStoryboard.Begin();
            }

            _isActive = true;
        }

        /// <summary>
        /// Bất hoạt Hexagon. Một số hành động có thể xảy ra như ẩn các nước có thể di chuyển,
        /// tắt animation...
        /// Lớp kế thừa có thể override phương thức này để cài đặt hành động riêng cho Hexagon đó.
        /// </summary>
        public virtual void Deactive()
        {
            HideMovableSlot();
            rotateStoryboard.Stop();
            _isActive = false;
        }

        /// <summary>
        /// Kiểm tra xem HexagonSlot có phải là hàng xóm của Hexagon hiện tại với khoảng cách 
        /// là distance hay không.
        /// </summary>
        /// <param name="hexagonSlot">HexagonSlot cần kiểm tra.</param>
        /// <param name="distance">Khoảng cách đến hàng xóm.</param>
        /// <returns>true nếu HexagonSlot là hàng xóm cách distance.</returns>
        public bool IsNeighbourWith(HexagonSlot hexagonSlot, int distance)
        {
            if (distance < 1)
            {
                throw new ArgumentOutOfRangeException("distance", "Phải lớn hơn hoặc bằng 1.");
            }

            HexagonSlot parent = VisualTreeHelper.GetParent(this) as HexagonSlot;
            if (parent == null)
            {
                throw new InvalidOperationException("Hexagon chưa được đặt vào HexagonSlot.");
            }

            if (parent.GetNeighbours(distance).Contains(hexagonSlot))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Bắt đầu sinh Hexagon mới ra từ Hexagon này.
        /// </summary>
        /// <param name="spawnedHexagon">Hexagon được sinh ra từ Hexagon này.</param>
        /// <param name="target">HexagonSlot chứa Hexagon mới được sinh</param>
        public void StartSpawnAnimation(Hexagon spawnedHexagon, HexagonSlot target)
        {
            _spawnedHexagon = spawnedHexagon;
            _targetSlot = target;

            scaleXAnimation.From = scaleYAnimation.From = 1;
            scaleXAnimation.To = scaleYAnimation.To = 0.5;

            backXEase.EasingMode = backYEase.EasingMode = EasingMode.EaseIn;

            // Chuyển nó sang trạng thái sinh nở.
            _state = HexagonActionState.Spawning;

            scaleStoryboard.Begin();
        }

        /// <summary>
        /// Bắt đầu animation hiển thị Hexagon đang được sinh ra.
        /// </summary>
        /// <param name="slot">HexagonSlot sẽ chứa Hexagon vừa được sinh ra này.</param>
        public void StartAppearAnimation(HexagonSlot slot)
        {
            slot.Hexagon = this;

            scaleXAnimation.From = scaleYAnimation.From = 0;
            scaleXAnimation.To = scaleYAnimation.To = 1;

            backXEase.EasingMode = backYEase.EasingMode = EasingMode.EaseOut;

            // Chuyển Hexagon sang trạng thái đang xuất hiện.
            _state = HexagonActionState.Appearing;

            OnAppearing(EventArgs.Empty);
            scaleStoryboard.Begin();

            rotateStoryboard.SpeedRatio = 5;
            rotateStoryboard.Begin();
        }

        /// <summary>
        /// Bắt đầu animation làm cho Hexagon biến mất khỏi màn hình và nhảy đến 1 HexagonSlot khác.
        /// </summary>
        /// <param name="target">Đích của việc nhảy.</param>
        public void StartJumpAnimation(HexagonSlot target)
        {
            _targetSlot = target;

            scaleXAnimation.From = scaleYAnimation.From = 1;
            scaleXAnimation.To = scaleYAnimation.To = 0;

            backXEase.EasingMode = backYEase.EasingMode = EasingMode.EaseIn;

            // Chuyển nó sang trạng thái nhảy nhót.
            _state = HexagonActionState.Jumping;

            scaleStoryboard.Begin();
        }
        #endregion
    }
}
